<html>
    <head>
        <title>将DbContext注册到依赖注入</title>
    </head>
    <body>
        <script>
             /*
                    三、将DbContext 注册到依赖注入



                            1、如何注册DbContext到IOC

                                        在模块中使用AddAbpDbContext方法为依赖注入系统注册你的DbContext类。

                                                using Microsoft.Extensions.DependencyInjection;
                                                using Volo.Abp.EntityFrameworkCore;
                                                using Volo.Abp.Modularity;

                                                namespace MyCompany.MyProject
                                                {
                                                    [DependsOn(typeof(AbpEntityFrameworkCoreModule))]
                                                    public class MyModule : AbpModule
                                                    {
                                                        public override void ConfigureServices(ServiceConfigurationContext context)
                                                        {
                                                            // 注册DbContext
                                                            context.Services.AddAbpDbContext<MyDbContext>();

                                                            //...
                                                        }
                                                    }
                                                }      
                                                    
                            2、添加默认存储库

                                        ABP可以自动为DbContext中的实体创建默认的通用存储库。只需在注册时使用AddDefaultRepositories()选项：

                                                services.AddAbpDbContext<MyDbContext>(options =>
                                                {
                                                    options.AddDefaultRepositories();
                                                });

                                        默认情况下，这将为每个聚合根实体（派生自AggregateRoot的类）创建一个存储库。

                                        如果你也想为其他实体创建存储库，请将includeAllEntities设置为true：

                                                services.AddAbpDbContext<MyDbContext>(options =>
                                                {
                                                    options.AddDefaultRepositories(includeAllEntities: true);
                                                });
                            
                                        然后，你可以在你的服务中注入并使用IRepository<TEntity, TPrimaryKey>。假设你有一个Book实体，其主键为Guid：

                                                public class Book : AggregateRoot<Guid>
                                                {
                                                    public string Name { get; set; }

                                                    public BookType Type { get; set; }
                                                }

                                        (BookType在这里是一个简单的enum并不重要）并且你想在域服务中创建一个新的Book实体:

                                                public class BookManager : DomainService
                                                {
                                                    private readonly IRepository<Book, Guid> _bookRepository;

                                                    // 注入默认仓储
                                                    public BookManager(IRepository<Book, Guid> bookRepository)
                                                    {
                                                        _bookRepository = bookRepository;
                                                    }

                                                    public async Task<Book> CreateBook(string name, BookType type)
                                                    {
                                                        Check.NotNullOrWhiteSpace(name, nameof(name));

                                                        var book = new Book
                                                        {
                                                            Id = GuidGenerator.Create(),
                                                            Name = name,
                                                            Type = type
                                                        };

                                                        // 使用标准的存储方法
                                                        await _bookRepository.InsertAsync(book);

                                                        return book;
                                                    }
                                                }

                            3、添加自定义存储库

                                       默认通用存储库在大多数情况下足够强大（因为它们实现了IQueryable）。

                                       但是，你可能需要创建一个自定义存储库来添加你自己的存储库方法。假设你想按类型删除所有书籍。

                                       建议为你的自定义存储库定义一个接口：

                                                public interface IBookRepository : IRepository<Book, Guid>
                                                {
                                                    Task DeleteBooksByType(BookType type);
                                                }
                                        你通常希望从IRepository派生以继承标准存储库方法（虽然，你不必这样做）。

                                        存储库接口在分层应用程序的 "领域层" 中定义。。在"基础设施层"中实现（EntityFrameworkCore启动模板中的项目）。

                                        IBookRepository接口的示例实现：

                                                public class BookRepository
                                                    : EfCoreRepository<BookStoreDbContext, Book, Guid>, IBookRepository
                                                {
                                                    public BookRepository(IDbContextProvider<BookStoreDbContext> dbContextProvider)
                                                        : base(dbContextProvider)
                                                    {
                                                    }

                                                    public async Task DeleteBooksByType(BookType type)
                                                    {
                                                        var dbContext = await GetDbContextAsync();
                                                        await dbContext.Database.ExecuteSqlRawAsync(
                                                            $"DELETE FROM Books WHERE Type = {(int)type}"
                                                        );
                                                    }
                                                }

                                        现在，可以注入IBookRepository并在需要时使用DeleteBooksByType方法。

                                    ★、覆盖默认通用仓储

                                            即使你创建了自定义存储库，你仍然可以注入默认的泛型存储库（IRepository<Book, Guid>对于本例）。默认存储库实现不会使用你创建的类。

                                            如果你想用自定义存储库替换默认存储库实现，请在 AddAbpDbContext 选项中执行：

                                                        context.Services.AddAbpDbContext<BookStoreDbContext>(options =>
                                                        {
                                                            options.AddDefaultRepositories();

                                                            // 指定仓储的实现类
                                                            options.AddRepository<Book, BookRepository>();
                                                        });

                                            当你想要覆盖基本存储库方法以对其进行自定义时，这一点尤其重要。

                                            例如，你可能希望覆盖DeleteAsync方法以更有效的方式删除特定实体：

                                                        public async override Task DeleteAsync(
                                                            Guid id,
                                                            bool autoSave = false,
                                                            CancellationToken cancellationToken = default)
                                                        {
                                                            //TODO: Custom implementation of the delete method
                                                        }
                */
        </script>
    </body>
</html>